package cc.lucien;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * 数据库操作工具类
 */
public class DBUtils<T> {

    //定义成员变量
    private static String driver;
    private static String url;
    private static String user;
    private static String password;

    private static Connection connection;
    private static PreparedStatement preparedStatement;
    private static ResultSet resultSet;

    static{
        // 1.加载数据库配置文件
        Properties properties = new Properties();
        InputStream inputStream = DBUtils.class.getResourceAsStream("db.properties");
        try {
            properties.load(inputStream);
            // 2.赋值
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            // 3.加载数据库驱动类
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
            System.err.println("加载数据库配置文件失败");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.err.println("加载数据库驱动类失败");
        }

    }

    /**
     * 获取数据库连接
     * @return
     */
    public Connection getConnection(){
        try {
            // 1.获取连接对象
            connection = DriverManager.getConnection(url, user, password);
            return connection;
        } catch (SQLException e) {
            e.printStackTrace();
            System.err.println("获取数据库连接对象失败");
        }
        return null;
    }

    /**
     * 从数据库查询数据
     * @param sql
     * @param params
     * @return
     */
    public ResultSet executeQuery(String sql, Object... params) {
        // 1.获取数据库连接
        getConnection();
        // 2.获取sql执行对象,预处理sql
        try {
            preparedStatement = connection.prepareStatement(sql);
            // 3.给占位符赋值
            setParams(params);
            // 4.执行sql并返回查询结果
            return preparedStatement.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 修改数据
     * @param sql
     * @param params
     * @return
     */
    public int executeUpdate(String sql, Object... params) {
        // 1.获取数据库连接
        getConnection();
        // 2.获取sql执行对象，预处理sql
        try {
            preparedStatement = connection.prepareStatement(sql);
            // 3.给占位符赋值
            setParams(params);
            // 4.执行sql并返回结果
            return preparedStatement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close();
        }
        return -1;
    }

    /**
     * 关闭连接，释放资源
     */
    public void close(){
        try {
            if (resultSet != null && !resultSet.isClosed()) {
                resultSet.close();
                resultSet = null;
            }
            if (preparedStatement != null && !preparedStatement.isClosed()) {
                preparedStatement.close();
                preparedStatement = null;
            }
            if (connection != null && !connection.isClosed()) {
                connection.close();
                connection = null;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static void close(ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
                resultSet = null;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询数据库，映射成JavaBean返回
     * @param sql
     * @param clazz
     * @param params
     * @return
     */
    public T selectOne(String sql, Class clazz, Object... params) {
        try {
            // 1.获取查询结果
            resultSet = executeQuery(sql, params);
            // 2.解析结果集
            if (resultSet.next()) {
                // 3.通过反射获取类对象
                Object object = parseData(resultSet, clazz);
                return (T) object;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close();
        }
        return null;
    }

    /**
     * 返回JavaBean集合
     * @param sql
     * @param clazz
     * @param params
     * @return
     */
    public List<T> selectAll(String sql, Class clazz, Object... params) {
        try{
            List<T> list = new ArrayList<>();
            // 1.获取查询结果集
            resultSet = executeQuery(sql,params);
            while (resultSet.next()) {
                // 2.通过反射创建对象，解析结果集
                Object object = parseData(resultSet, clazz);
                list.add((T) object);
            }
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            close();
        }
        return null;
    }

    /**
     * 解析结果集，返回一个对象
     * @param resultSet
     * @param clazz
     * @return
     */
    private T parseData(ResultSet resultSet, Class clazz){
        try{
            Object object = clazz.getConstructor(null).newInstance(null);
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {
                String columnName = metaData.getColumnName(i);
                String columnTypeName = metaData.getColumnTypeName(i);
                Object value = null;
                switch (columnTypeName) {
                    case "VARCHAR":
                        value = resultSet.getString(columnName);
                        break;
                    case "MEDIUMTEXT":
                        value = resultSet.getLong(columnName);
                        break;
                }
                // 4.给属性赋值
                // 5.获取setter方法
                String methodName = "set"+columnName.substring(0,1).toUpperCase()+columnName.substring(1);
                Field field = clazz.getDeclaredField(columnName);
                Method method = clazz.getMethod(methodName, field.getType());
                method.invoke(object, value);
            }
           return (T)object;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 给占位符赋值
     * @param params
     */
    private void setParams(Object... params) {
        if (params == null) {
            return;
        }
        for (int i = 0; i < params.length; i++) {
            try {
                preparedStatement.setObject(i+1, params[i]);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
